﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using System.Collections.Generic;
using Tesla.Content;
using Tesla.Core;
using Tesla.Math;

#pragma warning disable 1591

namespace Tesla.Graphics {
    public class Material : ISavable, IComparable<Material> {
        private String _name;
        private Effect _effect;
        private IEffectTechnique _currentTechnique;
        private long _sortID = -1;

        private Dictionary<String, MaterialParameter> _cachedParameters;
        private Dictionary<String, MaterialEngineParameter> _cachedEngineParameters;
        private Dictionary<String, IMaterialLogic> _materialLogic;

        private MaterialSamplerStateCollection _pixelSamplers;
        private MaterialSamplerStateCollection _vertexSamplers;
        private RenderStateMap _renderStateMap;

        public String Name {
            get {
                return _name;
            }
            set {
                _name = value;
            }
        }

        public Effect Effect {
            get {
                return _effect;
            }
        }

        public BlendState BlendState {
            get {
                return (BlendState) GetRenderState(RenderStateType.BlendState);
            }
            set {
                SetRenderState(value);
            }
        }

        public DepthStencilState DepthStencilState {
            get {
                return (DepthStencilState) GetRenderState(RenderStateType.DepthStencilState);
            }
            set {
                SetRenderState(value);
            }
        }

        public RasterizerState RasterizerState {
            get {
                return (RasterizerState) GetRenderState(RenderStateType.RasterizerState);
            }
            set {
                SetRenderState(value);
            }
        }

        public MaterialSamplerStateCollection SamplerStates {
            get {
                return _pixelSamplers;
            }
        }

        public MaterialSamplerStateCollection VertexSamplerStates {
            get {
                return _vertexSamplers;
            }
        }

        public int PassCount {
            get {
                return _currentTechnique.Passes.Count;
            }
        }

        public bool IsValid {
            get {
                return _effect != null;
            }
        }

        internal long SortID {
            get {
                if(_sortID == -1) {
                    RecomputeSortID();
                }
                return _sortID;
            }
        }

        public Material() {
            _name = "Material";
            _cachedEngineParameters = new Dictionary<String, MaterialEngineParameter>();
            _cachedParameters = new Dictionary<String, MaterialParameter>();
            _materialLogic = new Dictionary<String, IMaterialLogic>();
            _renderStateMap = new RenderStateMap();
            _pixelSamplers = new MaterialSamplerStateCollection(16, false);
            _vertexSamplers = new MaterialSamplerStateCollection(4, true);

            this.AddLogic(new LightLogic());
        }

        public Material(String name) {
            _name = name;
            _cachedEngineParameters = new Dictionary<String, MaterialEngineParameter>();
            _cachedParameters = new Dictionary<String, MaterialParameter>();
            _materialLogic = new Dictionary<String, IMaterialLogic>();
            _renderStateMap = new RenderStateMap();
            _pixelSamplers = new MaterialSamplerStateCollection(16, false);
            _vertexSamplers = new MaterialSamplerStateCollection(4, true);

            this.AddLogic(new LightLogic());
        }

        public void AddLogic(IMaterialLogic logic) {
            if(logic != null) {
                _materialLogic[logic.Name] = logic;
            }
        }

        public bool RemoveLogic(String logicName) {
            return _materialLogic.Remove(logicName);
        }

        public void EnableLogic(String logicID, bool enabled) {
            IMaterialLogic logic;
            if(_materialLogic.TryGetValue(logicID, out logic)) {
                logic.IsEnabled = enabled;
            }
        }

        public void ClearLogic() {
            _materialLogic.Clear();
        }

        private void ExecuteLogic(IRenderer renderer, IRenderable renderable) {
            foreach(KeyValuePair<String, IMaterialLogic> kv in _materialLogic) {
                kv.Value.Execute(this, renderer, renderable);
            }
        }

        public void SetActiveTechnique(String techniqueName) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueName];
                if(technique != null) {
                    _effect.CurrentTechnique = technique;
                    _currentTechnique = technique;
                    _renderStateMap.SetActiveRecords(technique);
                }
            }
        }

        public String GetActiveTechnique() {
            if(IsValid && _currentTechnique != null) {
                return _currentTechnique.Name;
            }
            return String.Empty;
        }

        public void SetRenderState(RenderState rs) {
            //Set to current technique record
            if(IsValid && _currentTechnique != null) {
                _renderStateMap.SetRenderState(_currentTechnique, 0, rs);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public RenderState GetRenderState(RenderStateType type) {
            if(IsValid && _currentTechnique != null) {
                return _renderStateMap.GetRenderState(type, _currentTechnique.Name, 0);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public void SetRenderState(RenderState rs, int techniqueIndex) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueIndex];
                if(technique != null) {
                    _renderStateMap.SetRenderState(technique, 0, rs);
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public RenderState GetRenderState(RenderStateType type, int techniqueIndex) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueIndex];
                if(technique != null) {
                    return _renderStateMap.GetRenderState(type, technique.Name, 0);
                } else {
                    return null;
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public void SetRenderState(RenderState rs, int techniqueIndex, String passName) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueIndex];
                if(technique != null) {
                    for(int i = 0; i < technique.Passes.Count; i++) {
                        IEffectPass pass = technique.Passes[i];
                        if(pass.Name.Equals(passName)) {
                            _renderStateMap.SetRenderState(technique, i, rs);
                            return;
                        }
                    }
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public RenderState GetRenderState(RenderStateType type, int techniqueIndex, String passName) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueIndex];
                if(technique != null) {
                    for(int i = 0; i < technique.Passes.Count; i++) {
                        IEffectPass pass = technique.Passes[i];
                        if(pass.Name.Equals(passName)) {
                            return _renderStateMap.GetRenderState(type, technique.Name, i);
                        }
                    }
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }

            return null;
        }

        public void SetRenderState(RenderState rs, int techniqueIndex, int passIndex) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueIndex];
                if(technique != null) {
                    _renderStateMap.SetRenderState(technique, passIndex, rs);
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public RenderState GetRenderState(RenderStateType type, int techniqueIndex, int passIndex) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueIndex];
                if(technique != null) {
                    return _renderStateMap.GetRenderState(type, technique.Name, passIndex);
                } else {
                    return null;
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public void SetRenderState(RenderState rs, String techniqueName) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueName];
                if(technique != null) {
                    _renderStateMap.SetRenderState(technique, 0, rs);
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public RenderState GetRenderState(RenderStateType type, String techniqueName) {
            if(IsValid) {
                return _renderStateMap.GetRenderState(type, techniqueName, 0);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public void SetRenderState(RenderState rs, String techniqueName, String passName) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueName];
                if(technique != null) {
                    for(int i = 0; i < technique.Passes.Count; i++) {
                        IEffectPass pass = technique.Passes[i];
                        if(pass.Name.Equals(passName)) {
                            _renderStateMap.SetRenderState(technique, i, rs);
                            return;
                        }
                    }
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public RenderState SetRenderState(RenderStateType type, String techniqueName, String passName) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueName];
                if(technique != null) {
                    for(int i = 0; i < technique.Passes.Count; i++) {
                        IEffectPass pass = technique.Passes[i];
                        if(pass.Name.Equals(passName)) {
                            return _renderStateMap.GetRenderState(type, techniqueName, i);
                        }
                    }
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }

            return null;
        }

        public void SetRenderState(RenderState rs, String techniqueName, int passIndex) {
            if(IsValid) {
                IEffectTechnique technique = _effect.Techniques[techniqueName];
                if(technique != null) {
                    _renderStateMap.SetRenderState(technique, passIndex, rs);
                }
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public RenderState GetRenderState(RenderStateType type, String techniqueName, int passIndex) {
            if(IsValid) {
                return _renderStateMap.GetRenderState(type, techniqueName, passIndex);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set RenderState. Call LoadEffect() first");
            }
        }

        public void LoadEffect(Effect effect) {
            if(effect == null) {
                throw new ArgumentNullException("Effect to load into material must be valid!");
            }

            //Dispose of old effect
            if(_effect != null) {
                _effect.Dispose();
                _effect = null;
            }

            _effect = effect;

            //Clear old cached data
            _pixelSamplers.Clear();
            _vertexSamplers.Clear();
            _renderStateMap.Clear();
            _cachedEngineParameters.Clear();
            _cachedParameters.Clear();

            RecomputeSortID();

            //Create a technique record for the default technique
            SetActiveTechnique(_effect.CurrentTechnique.Name);
        }

        public void SetEngineParameter(String paramName, EngineValue engineValue) {
            SetEngineParameter(paramName, false, engineValue);
        }

        public void SetEngineParameter(String paramName, bool isSemantic, EngineValue engineValue) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                //Doesn't exist, don't create an entry
                if(param == null) {
                    return;
                }
                MaterialEngineParameter engineParam = new MaterialEngineParameter(param, paramName, isSemantic, engineValue);
                _cachedEngineParameters[paramName] = engineParam;
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set engine parameter. Call LoadEffect() first.");
            }
        }

        private void ApplyEngineParameters(IRenderer renderer) {
            foreach(KeyValuePair<String, MaterialEngineParameter> kv in _cachedEngineParameters) {
                kv.Value.Apply(renderer);
            }
        }

        public void SetParameter(String paramName, float value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, float value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, value);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first.");
            }
        }

        public void SetParameter(String paramName, int value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, int value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, value);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first.");
            }
        }

        public void SetParameter(String paramName, bool value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, bool value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, value);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first");
            }
        }

        public void SetParameter(String paramName, Vector2 value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, Vector2 value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, value);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first");
            }
        }

        public void SetParameter(String paramName, Vector3 value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, Vector3 value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, value);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first.");
            }
        }

        public void SetParameter(String paramName, Vector4 value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, Vector4 value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, value);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first.");
            }
        }

        public void SetParameter(String paramName, Texture value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, Texture value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null || value == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, value);

                RecomputeSortID();
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first.");
            }
        }

        public void SetParameter(String paramName, Matrix value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, Matrix value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, value);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first.");
            }
        }

        public void SetParameter(String paramName, Quaternion value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, Quaternion value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }
                param.SetValue(value);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, new Vector4(value.X, value.Y, value.Z, value.W));
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first.");
            }
        }

        public void SetParameter(String paramName, Color value) {
            SetParameter(paramName, false, value);
        }

        public void SetParameter(String paramName, bool isSemantic, Color value) {
            if(IsValid) {
                IEffectParameter param = null;

                if(isSemantic) {
                    param = _effect.Parameters.GetParameterBySemantic(paramName);
                } else {
                    param = _effect.Parameters[paramName];
                }

                if(param == null) {
                    return;
                }

                Vector4 v = value.ToVector4();

                param.SetValue(v);
                _cachedParameters[paramName] = new MaterialParameter(param, paramName, isSemantic, v);
            } else {
                throw new ArgumentNullException("Material does not have a valid effect loaded, cannot set parameter. Call LoadEffect() first.");
            }
        }

        public void ClearParameterCache() {
            _cachedParameters.Clear();
        }

        public void ClearEngineParameterCache() {
            _cachedEngineParameters.Clear();
        }

        public void ClearCache() {
            ClearParameterCache();
            ClearEngineParameterCache();
        }

        public void ApplyMaterial(IRenderer renderer, IRenderable renderable) {
            if(_effect == null || renderable == null || renderer == null) {
                return;
            }

            //Setup the rendering process, first set our world matrix to the engine's value map
            Engine.ValueMap.WorldMatrix = renderable.WorldTransform.Matrix;

            ExecuteLogic(renderer, renderable);
            ApplyEngineParameters(renderer);

            _pixelSamplers.ApplyRenderStates(renderer);
            _vertexSamplers.ApplyRenderStates(renderer);
        }

        public void ApplyPass(IRenderer renderer, int index) {
            if(renderer == null) {
                return;
            }
            IEffectPass pass = _currentTechnique.Passes[index];
            pass.Apply();
            _renderStateMap.ApplyRenderStates(renderer, index);
        }

        #region IComparable<Material> Members

        public int CompareTo(Material other) {
             return (int) (SortID - other.SortID);
        }

        #endregion

        public Material Clone() {
            Material clone = new Material(_name);
            clone.LoadEffect(_effect.Clone());
            clone.SetActiveTechnique(_currentTechnique.Name);
            CloneEngineParameters(clone);
            CloneParameters(clone);
            clone._renderStateMap = _renderStateMap.Clone();
            clone._pixelSamplers = _pixelSamplers.Clone();
            clone._vertexSamplers = _vertexSamplers.Clone();
            return clone;
        }

        private void CloneEngineParameters(Material clone) {
            foreach(KeyValuePair<String, MaterialEngineParameter> kv in _cachedEngineParameters) {
                MaterialEngineParameter param = kv.Value;
                clone.SetEngineParameter(param.Name, param.IsSemantic, param.EngineValue);
            }
        }

        private void CloneLogic(Material clone) {
            foreach(KeyValuePair<String, IMaterialLogic> kv in _materialLogic) {
                clone.AddLogic(kv.Value);
            }
        }

        private void CloneParameters(Material clone) {
            foreach(KeyValuePair<String, MaterialParameter> kv in _cachedParameters) {
                MaterialParameter param = kv.Value;
                switch(param.ParameterType) {
                    case MaterialParameterType.Bool:
                        clone.SetParameter(param.Name, param.IsSemantic, param.BoolValue);
                        break;
                    case MaterialParameterType.Int32:
                        clone.SetParameter(param.Name, param.IsSemantic, param.IntValue);
                        break;
                    case MaterialParameterType.Single:
                        clone.SetParameter(param.Name, param.IsSemantic, param.SingleValue);
                        break;
                    case MaterialParameterType.Texture:
                        clone.SetParameter(param.Name, param.IsSemantic, param.TextureValue);
                        break;
                    case MaterialParameterType.Vector2:
                        clone.SetParameter(param.Name, param.IsSemantic, param.Vector2Value);
                        break;
                    case MaterialParameterType.Vector3:
                        clone.SetParameter(param.Name, param.IsSemantic, param.Vector3Value);
                        break;
                    case MaterialParameterType.Vector4:
                        clone.SetParameter(param.Name, param.IsSemantic, param.Vector4Value);
                        break;
                    case MaterialParameterType.Matrix:
                        clone.SetParameter(param.Name, param.IsSemantic, param.MatrixValue);
                        break;
                }
            }
        }

        private void RecomputeSortID() {
            if(_effect == null) {
                _sortID = -1;
                return;
            }
            _sortID = _effect.ResourceID * 32;
            foreach(KeyValuePair<String, MaterialParameter> kv in _cachedParameters) {
                MaterialParameter param = kv.Value;
                if(param.ParameterType == MaterialParameterType.Texture) {
                    Texture tex = param.TextureValue;
                    if(tex != null) {
                        _sortID += tex.ResourceID;
                    }
                }
            }
        }

        //For serializing a material into a TEM file
        internal Dictionary<String, MaterialParameter> GetMaterialParameters() {
            return _cachedParameters;
        }

        //For serializing a material into a TEM file
        internal Dictionary<String, MaterialEngineParameter> GetEngineParameters() {
            return _cachedEngineParameters;
        }

        /// <summary>
        /// Serializes the object and writes it to the output.
        /// </summary>
        /// <param name="output">Savable Output</param>
        public void Write(ISavableWriter output) {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Deserializes the object and populates it from the input.
        /// </summary>
        /// <param name="input">Savable input</param>
        public void Read(ISavableReader input) {
            throw new NotImplementedException();
        }
    }
}
